#include <windows.h>
//#include <stdexcept>
#include "Service.h"
#include "config.h"
#include "control.h"
#include "Log.h"

namespace wrservice
{

Service* Service::p_instance_ = 0;
bool     Service::is_destroyed_ = false;

/////////////////////////////////////////////////////////////////////
//
// Goal : 
//   Entry point for the service
//
void WINAPI service_main (
        __in DWORD  dwArgc,
        __in LPWSTR *lpszArgv
        )
{
    Service& svc = Service::instance();

    svc.set_status_handle ( ::RegisterServiceCtrlHandler(
        svc.get_name(), &service_control_handler ) );

    if( !svc.get_status_handle() )
    {
        svc.get_log() << "RegisterServiceCtrlHandler failed";
        //throw std::runtime_error ("RegisterServiceCtrlHandler failed");
        return;
    }

    //this->status_.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 
    //this->status_.dwServiceSpecificExitCode = 0;

    // Report initial status to the SCM
    svc.update_status( SERVICE_START_PENDING, NO_ERROR, 3000 );

    service_main_init( dwArgc, lpszArgv );
}

/////////////////////////////////////////////////////////////////////
//
// Goal : 
//   The service initialization and main code
//
void WINAPI service_main_init (
    __in DWORD	dwArgc, 
    __in LPWSTR	*lpszArgv
    )
{
    UNREFERENCED_PARAMETER (dwArgc);
    UNREFERENCED_PARAMETER (lpszArgv);

    // TO_DO: Declare and set any required variables.
    //   Be sure to periodically call svc.update_status() with 
    //   SERVICE_START_PENDING. If initialization fails, call
    //   svc.update_status with SERVICE_STOPPED.
    Service& svc = Service::instance();

    svc.set_stop_event ( ::CreateEvent (NULL, TRUE, FALSE, NULL) );

    if ( svc.get_stop_event() == NULL)
    {
        svc.get_log() << "CreateEvent failed";
        //throw std::runtime_error ("CreateEvent failed");
        svc.update_status (SERVICE_STOPPED, NO_ERROR, 0);
        return;
    }
    // initialization is complete.

    svc.update_status (SERVICE_RUNNING, NO_ERROR, 0);
	
    // TO_DO: Perform work until service stops.
    service_start_thread ();
    if ( !svc.get_state() ) {
        svc.get_log() << "Service::start_thread_ failed";
        //throw std::runtime_error ("Service::start_thread_ failed");
        svc.update_status (SERVICE_STOPPED, NO_ERROR, 0);
    }

    ::WaitForSingleObject (svc.get_stop_event(), INFINITE);
    svc.update_status (SERVICE_STOPPED, NO_ERROR, 0);
}

/////////////////////////////////////////////////////////////////////
//
// Goal : 
//   Called by SCM whenever a control code is sent to the service
//   using the ControlService function.
//	 Handle the requested control code.
//
void WINAPI service_control_handler (
    __in DWORD dwCtrl
    )
{
    switch (dwCtrl) 
    {  
        case SERVICE_CONTROL_STOP: 
        case SERVICE_CONTROL_SHUTDOWN:
        {
            Service& svc = Service::instance();
            svc.update_status(SERVICE_STOP_PENDING, NO_ERROR, 0);

            // Signal the service to stop.
            svc.set_state(false);
            ::SetEvent( svc.get_stop_event() );
            svc.update_status(svc.get_status().dwCurrentState, NO_ERROR, 0);

            return;
        }
        default:
            break;
    }
}

/////////////////////////////////////////////////////////////////////
//
// Goal : 
//   Create new thread for main program logic
//
void WINAPI service_start_thread ()
{
    DWORD id;
    HANDLE hServiceThread = 
        ::CreateThread(0,0,
        (LPTHREAD_START_ROUTINE) &service_execution_thread,
            0,0,&id);
    Service& svc = Service::instance();

    svc.set_state (
        ( hServiceThread != NULL ) ? true : false );
}

/////////////////////////////////////////////////////////////////////
//
// Goal : 
//   Main program logic
//
DWORD WINAPI service_execution_thread (
    __in LPDWORD param
    )
{
    UNREFERENCED_PARAMETER (param);

    Service& svc = Service::instance();

    // !!! need to be repaired
    ::Sleep(5000);	// !!! wait until SvcStartControl() ends
    // TO_DO: main actions to perform
    while( svc.get_state() )
    {
        svc.get_log().write_event ( "SvcExecutionThread running..." );
        ::Sleep(5000);
    }
    return 0;
}

/////////////////////////////////////////////////////////////////////
//
// Goal : 
//   Sets the current service status and reports it to the SCM.
//
void Service::update_status (
    __in DWORD dwCurrentState,
    __in DWORD dwWin32ExitCode,
    __in DWORD dwWaitHint
    )
{
    static DWORD dwCheckPoint = 1;

    this->status_.dwCurrentState  = dwCurrentState;
    this->status_.dwWin32ExitCode = dwWin32ExitCode;
    this->status_.dwWaitHint      = dwWaitHint;

    // ???
    if (dwCurrentState == SERVICE_START_PENDING)
        this->status_.dwControlsAccepted = 0;
    else 
        this->status_.dwControlsAccepted = 
            SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;

    if ( (dwCurrentState == SERVICE_RUNNING) ||
         (dwCurrentState == SERVICE_STOPPED) )
        this->status_.dwCheckPoint = 0;
    else
        this->status_.dwCheckPoint = dwCheckPoint++;

    SetServiceStatus( this->status_handle_, &this->status_ );
}

void Service::config(service_config_t con)
{
    switch (con)
    {
        case install:
            service_install (*this);
            break;
        case uninstall:
            service_uninstall (*this);
            break;
        case start:
            service_start (*this);
            break;
        case stop:
            service_stop (*this);
            break;
        case help:
            service_usage (*this);
            break;
        case none:
            break;
        default:
            throw std::runtime_error ("Undefined config option");
    }
}


void Service::execute ()
{
    SERVICE_TABLE_ENTRY DispatchTable[] = 
    { 
        { this->name_, (LPSERVICE_MAIN_FUNCTIONW) service_main },
        { NULL, NULL }
    }; 
 
    // This call returns when the service has stopped
    if ( !::StartServiceCtrlDispatcher ( DispatchTable ) )
    {
        this->get_log() << "StartServiceCtrlDispatcher failed";
        //throw std::runtime_error ("StartServiceCtrlDispatcher failed");
    }
}

void service_config (Service& svc, LPCWSTR con)
{
    service_config_t econ = bad;
    
    if ( lstrcmpiW ( con, L"install") == 0 )
        econ = install;
    else if ( lstrcmpiW ( con, L"uninstall") == 0 )
        econ = uninstall;
    else if ( lstrcmpiW ( con, L"start") == 0 )
        econ = start;
    else if ( lstrcmpiW ( con, L"stop") == 0 )
        econ = stop;
    else if ( lstrcmpiW ( con, L"help") == 0 )
        econ = help;
    else if ( lstrcmpiW ( con, L"") == 0 )
        econ = none;
    svc.config (econ);
}

} /* namespace wrservice */
